﻿using System;
using System.Collections.Generic;
using System.IO;
using System.IO.IsolatedStorage;
using System.Net;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Xml.Linq;
using Components.WorkflowDesignSvc;

namespace Components
{
    public class WorkflowDesign
    {
        public delegate void OnPublishedWorkflowsUpdated(List<string> workflowNames);
        public event OnPublishedWorkflowsUpdated PublishedWorkflowsUpdated;

        public delegate void OnPublishedWorkflowOpened(WorkflowDesign wfDesign);
        public event OnPublishedWorkflowOpened PublishedWorkflowOpened;

        #region props
        public Guid WorkflowID { get; set; }
        public string WorkflowName { get; set; }
        public string FileName { get; set; }
        public Version Version { get; set; }
        public string Author { get; set; }
        public DateTime CreatedOn { get; set; }
        public DateTime ModifiedOn { get; set; }
        public Dictionary<Guid, WorkflowComponentBase> Components { get; set; }
        public Dictionary<Guid, List<Guid>> SuccessConnectors { get; set; }
        public Dictionary<Guid, List<Guid>> FailureConnectors { get; set; }
        #endregion

        public WorkflowDesign()
        {
            this.WorkflowID = Guid.NewGuid();
            this.WorkflowName = "New workflow";
            this.FileName = "New workflow.wox";
            this.Version = new Version(1, 0, 0, 0);
            this.CreatedOn = DateTime.Now;
            this.ModifiedOn = this.CreatedOn;
            this.Author = string.Empty;
            this.Components = new Dictionary<Guid, WorkflowComponentBase>();
            this.SuccessConnectors = new Dictionary<Guid, List<Guid>>();
            this.FailureConnectors = new Dictionary<Guid, List<Guid>>();
        }

        public WorkflowDesign(string author)
            : this()
        {
            this.Author = author;
        }

        public XDocument Save()
        {
            this.ModifiedOn = DateTime.Now;
            XElement xml = new XElement(
                "Workflow",
                new XAttribute("WorkflowID", this.WorkflowID.ToString()),
                new XAttribute("WorkflowName", this.WorkflowName),
                new XAttribute("FileName", this.FileName),
                new XAttribute("Version",this.Version.ToString()),
                new XAttribute("Author", this.Author),
                new XAttribute("CreatedOn", this.CreatedOn.ToString()),
                new XAttribute("ModifiedOn", this.ModifiedOn.ToString()));
            // nodes
            XElement componentRootElement = new XElement("Components");
            foreach (WorkflowComponentBase component in this.Components.Values)
            {
                XElement xe = component.Save();
                componentRootElement.Add(xe);
            }
            xml.Add(componentRootElement);

            // connectors
            XElement connectorRootElement = new XElement("Connectors");
            foreach (Guid fromID in this.SuccessConnectors.Keys)
            {
                List<Guid> toIDs = this.SuccessConnectors[fromID];
                foreach (Guid toID in toIDs)
                {
                    XElement xe = new XElement("Connector",
                        new XAttribute("FromComponentID", fromID.ToString()),
                        new XAttribute("ToComponentID", toID.ToString()),
                        new XAttribute("FromPortType", PortType.SuccessOutput));
                    connectorRootElement.Add(xe);
                }
            }
            foreach (Guid fromID in this.FailureConnectors.Keys)
            {
                List<Guid> toIDs = this.FailureConnectors[fromID];
                foreach (Guid toID in toIDs)
                {
                    XElement xe = new XElement(
                        "Connector",
                        new XAttribute("FromComponentID", fromID.ToString()),
                        new XAttribute("ToComponentID", toID.ToString()),
                        new XAttribute("FromPortType", PortType.ErrorOutput));
                    connectorRootElement.Add(xe);
                }
            }
            xml.Add(connectorRootElement);

            XDocument xDoc = new XDocument(
                new XDeclaration("1.0", "utf-8", "yes"),
                new XComment("Workflow design created by Xiaodong"),
                xml);
            return xDoc;
        }

        public static WorkflowDesign Read(XDocument xDoc)
        {
            WorkflowDesign wfDesign = new WorkflowDesign();
            XElement root = xDoc.Element("Workflow");
            wfDesign.WorkflowID = new Guid(root.Attribute("WorkflowID").Value);
            wfDesign.WorkflowName = root.Attribute("WorkflowName").Value;
            wfDesign.FileName = root.Attribute("FileName").Value;
            wfDesign.Version = new Version(root.Attribute("Version").Value);
            wfDesign.Author = root.Attribute("Author").Value;
            wfDesign.CreatedOn = DateTime.Parse(root.Attribute("CreatedOn").Value);
            wfDesign.ModifiedOn = DateTime.Parse(root.Attribute("ModifiedOn").Value);
            foreach (XElement xe in root.Elements("Components").Elements("Component"))
            {
                string typeName = xe.Attribute("Type").Value;
                Type type = WorkflowEngine.ComponentTypes[typeName];
                WorkflowComponentBase component = (WorkflowComponentBase)Activator.CreateInstance(type);
                component = component.Instantiate(type, xe);
                wfDesign.Components.Add(component.ComponentID, component);
            }
            foreach (XElement xe in root.Elements("Connectors").Elements("Connector"))
            {
                Guid fromID = new Guid(xe.Attribute("FromComponentID").Value);
                Guid toID = new Guid(xe.Attribute("ToComponentID").Value);
                PortType portType = (PortType)Enum.Parse(typeof(PortType), xe.Attribute("FromPortType").Value, false);
                if (portType == PortType.SuccessOutput)
                {
                    if (wfDesign.SuccessConnectors.ContainsKey(fromID))
                    {
                        List<Guid> toIDs = wfDesign.SuccessConnectors[fromID];
                        toIDs.Add(toID);
                        wfDesign.SuccessConnectors[fromID] = toIDs;
                    }
                    else
                    {
                        List<Guid> toIDs = new List<Guid>();
                        toIDs.Add(toID);
                        wfDesign.SuccessConnectors.Add(fromID, toIDs);
                    }
                }
                else
                {
                    if (wfDesign.FailureConnectors.ContainsKey(fromID))
                    {
                        List<Guid> toIDs = wfDesign.FailureConnectors[fromID];
                        toIDs.Add(toID);
                        wfDesign.FailureConnectors[fromID] = toIDs;
                    }
                    else
                    {
                        List<Guid> toIDs = new List<Guid>();
                        toIDs.Add(toID);
                        wfDesign.FailureConnectors.Add(fromID, toIDs);
                    }
                }
            }

            return wfDesign;
        }

        public static WorkflowDesign Read(System.IO.FileInfo file)
        {
            string fileName = file.Name;
            using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication())
            {
                using (IsolatedStorageFileStream isfs = new IsolatedStorageFileStream(fileName, FileMode.Create, isf))
                {
                    byte[] buffer = new byte[file.Length];
                    BinaryReader reader = new BinaryReader(file.OpenRead());
                    reader.Read(buffer, 0, buffer.Length);
                    reader.Close();
                    isfs.Write(buffer, 0, buffer.Length);
                    isfs.Close();
                    string workflowName = fileName;
                    if (workflowName.IndexOf(".wox") > 0)
                        workflowName = workflowName.Substring(0, workflowName.IndexOf(".wox"));
                    XDocument xDoc = WorkflowFALC.ReadWorkflow(workflowName);
                    return WorkflowDesign.Read(xDoc);
                }
            }
        }

        public string XmlData
        {
            get
            {
                XDocument xDoc = this.Save();
                return xDoc.ToString();
            }
        }

        public static WorkflowDesign Read(string xmlData)
        {
            XDocument xDoc = XDocument.Parse(xmlData);
            return WorkflowDesign.Read(xDoc);
        }

        public void Publish()
        {
            WorkflowDesignServiceClient client = new WorkflowDesignServiceClient();
            string xml = this.Save().ToString();
            client.PublishWorkflowAsync(xml);
        }

        public void GetPublishedWorkflowDesigns()
        {
            WorkflowDesignServiceClient client = new WorkflowDesignServiceClient();
            client.ListWorkflowNamesCompleted += new EventHandler<ListWorkflowNamesCompletedEventArgs>(client_ListWorkflowNamesCompleted);
            client.ListWorkflowNamesAsync();
        }

        void client_ListWorkflowNamesCompleted(object sender, ListWorkflowNamesCompletedEventArgs e)
        {
            List<string> publishedWorkflowNames =new List<string>();
            foreach (string workflowName in e.Result)
            {
                publishedWorkflowNames.Add(workflowName);
            }
            if (this.PublishedWorkflowsUpdated != null)
                this.PublishedWorkflowsUpdated(publishedWorkflowNames);
        }

        public void GetPublishedWorkflow(string workflowName)
        {
            WorkflowDesignServiceClient client = new WorkflowDesignServiceClient();
            client.GetWorkflowByNameCompleted += new EventHandler<GetWorkflowByNameCompletedEventArgs>(client_GetWorkflowByNameCompleted);
            client.GetWorkflowByNameAsync(workflowName);
        }

        void client_GetWorkflowByNameCompleted(object sender, GetWorkflowByNameCompletedEventArgs e)
        {
            WorkflowDesign wfDesign = WorkflowDesign.Read(e.Result);
            if (this.PublishedWorkflowOpened != null)
                this.PublishedWorkflowOpened(wfDesign);
        }

        public void RunWorkflow()
        {
            WorkflowDesignServiceClient client = new WorkflowDesignServiceClient();
            client.TestWorkflowAsync(this.WorkflowID);
        }
    }
}
